package org.ricks.db;

import org.ricks.common.bean.ArrayUtil;
import org.ricks.ioc.Table;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @author chenwei
 * @Title:
 * @Package
 * @Description:
 * @date 2020/12/211:30
 */
public class TableMetaBuilder {

    public static TableMeta getTableMeta(Class<?> clazz) {
        TableMeta tableMeta = new TableMeta();
        Table table = clazz.getAnnotation(Table.class);
        Objects.requireNonNull(table, String.format("entity need use @Table - %s", clazz.getName()));
        if (table.name().length() == 0) {
            tableMeta.setName(clazz.getSimpleName());
        } else {
            tableMeta.setName(table.name());
        }
        if (table.charset().length() > 0) {
            tableMeta.setCharset(table.charset());
            if (table.collate().length() > 0) {
                tableMeta.setCollate(table.collate());
            }
        }
        tableMeta.setComment(table.comment());
        tableMeta.setColumns(new LinkedList());
        tableMeta.setIndexKeys(new HashSet());
        Class tempClass = clazz;
        while (tempClass !=null && !tempClass.getName().toLowerCase().equals("java.lang.object")) {
            Field[] fields = tempClass.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                Column column = field.getAnnotation(Column.class);
                if (column != null) {
                    PrimaryKey primaryKey = field.getAnnotation(PrimaryKey.class);
                    if (primaryKey != null) {
                        if (tableMeta.getPrimaryColumn() != null) {
                            throw new RuntimeException(String.format("only one primary key - %s", clazz.getName()));
                        }
                        TableMeta.ColumnMeta columnMeta = getColumnMate(field);
                        tableMeta.setPrimaryColumn(columnMeta);
                        tableMeta.getColumns().add(columnMeta);
                        if (TableSupport.isJavaBean(tableMeta.getPrimaryColumn())) {
                            throw new RuntimeException("primary key type not be java bean");
                        }
                    } else {
                        TableMeta.ColumnMeta columnMeta = getColumnMate(field);
                        tableMeta.getColumns().add(columnMeta);
                        IndexKey indexKey = field.getAnnotation(IndexKey.class);
                        if (indexKey != null) {
                            tableMeta.getIndexKeys().add(columnMeta);
                        }
                    }
                }
            }
            tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
        }
        Map<String, TableMeta.ColumnMeta> columnMetaMap = new HashMap();
        columnMetaMap.put(tableMeta.getPrimaryColumn().getName(), tableMeta.getPrimaryColumn());
        tableMeta.getColumns().forEach(columnMeta -> columnMetaMap.put(columnMeta.getName(), columnMeta));
        tableMeta.setColumnMetaMap(columnMetaMap);
        return tableMeta;
    }

    private static TableMeta.ColumnMeta getColumnMate(Field field) {
        TableMeta.ColumnMeta columnMeta = new TableMeta.ColumnMeta();
        Column column = field.getAnnotation(Column.class);
        columnMeta.setName(field.getName());
        ColumnType columnType;
        if (column.type() == ColumnType.DEFAULT) {
            columnType = getColumnType(field, column);
//            if (columnType == ColumnType.CHAR || columnType == ColumnType.VARCHAR) {
//                columnMeta.setType(String.format("%s(%s)", columnType.name(), column.length()));
//            } else {
//                columnMeta.setType(columnType.name());
//            }
        } else {
            columnType = column.type();
            columnMeta.setType(column.type().name());
        }
        if (columnType == ColumnType.CHAR || columnType == ColumnType.VARCHAR) {
            columnMeta.setType(String.format("%s(%s)", columnType.name(), column.length()));
        } else {
            columnMeta.setType(columnType.name());
        }
        if (columnMeta.getType().toUpperCase().endsWith("INT")) {
            columnMeta.setIntNumber(true);
        }
        columnMeta.setReadOnly(column.readOnly());
        columnMeta.setField(field);
        if(List.class.isAssignableFrom(field.getType())) {
            Type type = field.getGenericType();
            Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
            if (actualTypeArguments != null && actualTypeArguments.length > 0) {
                 columnMeta.setFieldGenericityClazz((Class<?>) actualTypeArguments[0]);
            }
        }

        columnMeta.setNonNull(column.nonNull());
        columnMeta.setUnsigned(column.unsigned());
        PrimaryKey primaryKey = field.getAnnotation(PrimaryKey.class);
        if (primaryKey != null) {
            if (columnMeta.isIntNumber()) {
                columnMeta.setAuto(primaryKey.auto());
            }
            columnMeta.setPrimaryKey(true);
            columnMeta.setNonNull(true);
        }
        IndexKey indexKey = field.getAnnotation(IndexKey.class);
        if (indexKey != null) {
            columnMeta.setIndex(indexKey.index());
        }
        columnMeta.setComment(column.comment());

        columnMeta.setDefaultValue(getDefaultValue(field, columnType));
        return columnMeta;
    }

    /**
     * Map List 直接用mysql JSON格式
     * @param field
     * @param column
     * @return
     */
    private static ColumnType getColumnType(Field field, Column column) {
        Class<?> type = field.getType();
        if (type == boolean.class || type == Boolean.class)
            return ColumnType.TINYINT;
        if (type == byte.class || type == Byte.class)
            return ColumnType.TINYINT;
        if (type == short.class || type == Short.class)
            return ColumnType.SMALLINT;
        if (type == int.class || type == Integer.class)
            return ColumnType.INT;
        if (type == long.class || type == Long.class)
            return ColumnType.BIGINT;
        if (type == float.class || type == Float.class)
            return ColumnType.FLOAT;
        if (type == double.class || type == Double.class)
            return ColumnType.DOUBLE;
        if (type == Date.class || type == LocalDateTime.class)
            return ColumnType.DATETIME;
        if (type == Enum.class)
            return ColumnType.INT;
        if (Map.class.isAssignableFrom(type))
            return ColumnType.JSON;
        if (type.isArray() && (type.getComponentType() == byte.class || type.getComponentType() == Byte.class)) //字节数组类型
            return ColumnType.BLOB;
        int length = column.length();
        if (length <= 255)
            return ColumnType.CHAR;
        if (length <= 16383)
            return ColumnType.VARCHAR;
        return ColumnType.TEXT;
    }

    private static String getDefaultValue(Field field, ColumnType columnType) {
        if (columnType == ColumnType.BLOB || columnType == ColumnType.LONGBLOB)
            return null;
        if (columnType == ColumnType.TEXT || columnType == ColumnType.LONGTEXT)
            return null;
        Class<?> type = field.getType();
        if (type == boolean.class)
            return "0";
        if (type == byte.class)
            return "0";
        if (type == short.class)
            return "0";
        if (type == int.class)
            return "0";
        if (type == long.class)
            return "0";
        if (type == float.class)
            return "0";
        if (type == double.class)
            return "0";
//        if (type.isArray() || Collection.class.isAssignableFrom(type))
//            return "[]";
//        if (Map.class.isAssignableFrom(type))
//            return "{}";
        return null;
    }
}
